package ace.module.common.core.impl.manager.helper.impl;

import ace.cmp.spring.data.jdbc.querydsl.core.QuerydslRelationHelper;
import ace.module.common.core.impl.annotation.appout.AppId;
import ace.module.common.core.impl.manager.helper.ManagerHelper;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.dsl.SimpleExpression;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

/**
 * 应用业务辅助接口.
 *
 * @author Caspar
 */
@Component
public class ManagerHelperImpl implements ManagerHelper {

  private final QuerydslRelationHelper querydslRelationHelper;

  public ManagerHelperImpl(QuerydslRelationHelper querydslRelationHelper) {
    this.querydslRelationHelper = querydslRelationHelper;
  }

  @Override
  public SimpleExpression findExpressionByAnnClassOrFieldName(Class<?> entityClass, Class<? extends Annotation> annClass,
      String fieldName) {
    Path<?> result = this.querydslRelationHelper
        .getPathByEntityTypeAndAnnotation(entityClass, annClass);

    if (Objects.isNull(result)) {
      Field field = this.querydslRelationHelper.getFieldByName(entityClass, fieldName);
      result = Optional.ofNullable(field)
          .map(Field::getName)
          .map(name -> this.querydslRelationHelper
              .getPathByEntityTypeAndFieldName(entityClass, name)
          )
          .orElse(null);
    }

    Assert.notNull(result, fieldName + " property cant");

    return (SimpleExpression) result;
  }

  @Override
  public Field findFieldByAnnClassOrFieldName(Class<?> entityClass, Class<? extends Annotation> annClass,
      String fieldName) {
    Field result = this.querydslRelationHelper
        .getFieldByAnnotation(entityClass, annClass);

    if (Objects.isNull(result)) {
      Field field = this.querydslRelationHelper.getFieldByName(entityClass,
          fieldName);
      result = Optional.ofNullable(field).orElse(null);
    }

    Assert.notNull(result, entityClass.getName() + " " + fieldName + " property can not init");

    return result;
  }

  @Override
  public Field findAppIdField(Class<?> entityClass) {
    Field field = this.querydslRelationHelper.getFieldByAnnotation(entityClass, AppId.class);
    if (field != null) {
      return field;
    }

    List<Field> fields = FieldUtils.getAllFieldsList(entityClass)
        .stream()
        .filter(p -> p.getName().endsWith("AppId") || p.getName().endsWith("appId"))
        .collect(Collectors.toList());
    if (fields.size() == 1) {
      field = fields.get(0);
    }
    Assert.notNull(field, entityClass.getName() + " can not init entityAppIdField property");

    return field;
  }

}
